package com.he.service.impl;

import cn.hutool.captcha.generator.RandomGenerator;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.he.producer.EmailProducer;
import com.he.constant.CommonConstant;
import com.he.constant.RedisConstant;
import com.he.domain.Result;
import com.he.domain.dto.LoginDTO;
import com.he.domain.dto.MailDTO;
import com.he.domain.dto.RegisterDTO;
import com.he.entity.LoginUser;
import com.he.entity.User;
import com.he.entity.UserDTO;
import com.he.entity.UserRole;
import com.he.enums.LoginTypeEnum;
import com.he.enums.RoleEnum;
import com.he.mapper.UserMapper;
import com.he.mapper.UserRoleMapper;
import com.he.service.LoginService;
import com.he.service.RedisService;
import com.he.utils.IpUtils;
import com.he.utils.JwtUtil;
import com.he.utils.SecurityUtils;
import com.he.utils.UserAgentUtils;
import java.time.LocalDateTime;

import jakarta.annotation.Resource;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import jakarta.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static com.he.utils.CommonUtils.checkEmail;

/**
 * @author He
 * @version 1.0
 * @Date 2023/9/7 15:52
 * @Desc 登录业务实现类
 */
@Service
public class LoginServiceImpl implements LoginService {


    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private RedisService redisService;

    @Resource
    private EmailProducer emailProducer;

    @Resource
    private UserMapper userMapper;

    @Resource
    private UserRoleMapper userRoleMapper;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private HttpServletRequest request;

    @Override
    public Result<?> login(LoginDTO login) {
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(login.getUsername(), login.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        //1、判断是否认证通过
        if (Objects.isNull(authenticate)){
            throw new RuntimeException("用户名或密码错误");
        }
        //2、获取userid生成Token
        LoginUser loginUser = (LoginUser) authenticate.getPrincipal();
        String userId = loginUser.getUser().getId().toString();
        String jwt = JwtUtil.createJWT(userId);
        loginUser.getUser().setIpAddress(request.getRemoteAddr());
        loginUser.getUser().setIpSource("");
        loginUser.setOs(UserAgentUtils.parseOs(request.getHeader("User-Agent")));
        loginUser.setBrowser(UserAgentUtils.parseBrowser(request.getHeader("User-Agent")));
        loginUser.setLoginTime(LocalDateTime.now());
        //3、把用户信息存入redis
        redisService.setObject(RedisConstant.LOGIN_USER_KEY_PREFIX + userId, loginUser, CommonConstant.SESSION_EXPIRE_TIME, TimeUnit.HOURS);
        HashMap<String, String> map = new HashMap<>();
        map.put("token", jwt);
        return Result.success(map);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void register(RegisterDTO register) {
        verifyCode(register.getUsername(), register.getCode());
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
                .select(User::getUsername)
                .eq(User::getUsername, register.getUsername()));
        Assert.isNull(user, "邮箱已注册！");
        User newUser = User.builder()
                .username(register.getUsername())
                .password(passwordEncoder.encode(register.getPassword()))
                .email(register.getUsername())
                .avatar("http://s05u67537.hn-bkt.clouddn.com/vue-blog/08/30/_20230830223649.jpg")
                .nickname(register.getUsername())
                .loginType(LoginTypeEnum.EMAIL.getLoginType())
                .isDisable(CommonConstant.FALSE)
                .build();
        userMapper.insert(newUser);
        //绑定用户角色
        UserRole userRole = UserRole.builder()
                .userId(newUser.getId())
                .roleId(RoleEnum.USER.getRoleId())
                .build();
        userRoleMapper.insert(userRole);
    }

    @Resource
    private RabbitTemplate rabbitTemplate;
    @Override
    public void sendCode(String username) {
        Assert.isTrue(checkEmail(username), "请输入正确的邮箱！");
        RandomGenerator randomGenerator = new RandomGenerator("0123456789", 6);
        String code = randomGenerator.generate();
        Map<String, Object> contentMap = new HashMap<>(7);
        contentMap.put("siteLogo","https://foruda.gitee.com/avatar/1682152567855938402/10784427_ruyi-he_1682152567.png");
        contentMap.put("code", code);
        contentMap.put("expireTime", RedisConstant.CODE_EXPIRE_TIME);
        contentMap.put("username", username);
        MailDTO mailDTO = MailDTO.builder()
                .toEmail(username)
                .subject(CommonConstant.CAPTCHA)
                .template(CommonConstant.REGISTER_TEMPLATE)
                .contentMap(contentMap)
                .build();
        //生产者发送消息
        emailProducer.sendHtmlEmailMessage(mailDTO);
        System.out.println("生产者发送消息了 验证码为：" + code);
        //验证码存入Redis
        redisService.setObject(RedisConstant.VERIFY_CODE_KEY + username, code, RedisConstant.CODE_EXPIRE_TIME * 60, TimeUnit.SECONDS);
    }

    @Override
    public void logout() {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (Objects.nonNull(loginUser)){
            redisService.deleteObject(RedisConstant.LOGIN_USER_KEY_PREFIX + loginUser.getUser().getId());
        }
    }

    @Override
    public void updatePassword(UserDTO updateDTO) {
        verifyCode(updateDTO.getUsername(), updateDTO.getCode());
        User user = userMapper.selectOne(new LambdaQueryWrapper<User>()
                .select(User::getUsername)
                .eq(User::getUsername, updateDTO.getUsername()));
        Assert.notNull(user, "邮箱尚未注册！");
        userMapper.update(new User(),new LambdaUpdateWrapper<User>()
                .set(User::getPassword, passwordEncoder.encode(updateDTO.getPassword()))
                .eq(User::getUsername, updateDTO.getUsername()));
    }

    /**
     * @Description: verifyCode 验证码校验
     * @Param: username
     * @param code
     * @return: void
     * */
    public void verifyCode(String username, String code) {
        String sysCode = redisService.getObject(RedisConstant.VERIFY_CODE_KEY + username);
        Assert.notBlank(sysCode, "验证码未发送或已过期！");
        Assert.isTrue(sysCode.equals(code), "验证码错误，请重新输入！");
    }
}
